﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.Design;
using Needle.Utils;
using Needle.Utils.Collections;
using Needle.Utils.Extensions;
using System.Diagnostics;
namespace Needle.Core.Lifetime
{
    /// <summary></summary>
    [DebuggerDisplay("LifetimeManager {Name} ( tracking {_Trackers.Count} instances )")]
    public class LifetimeManager : NeedleContext , ILifetimeManager
    {
        /// <summary></summary>
        public LifetimeManager()
        { InitializeComponent(); }

        /// <summary></summary>
        public LifetimeManager(IContainer container)
            : base(container)
        { InitializeComponent(); }

        /// <summary></summary>
        public override void BeginInit()
        {
            base.BeginInit();
            this.GetService<IServiceContainer>().AddService<ILifetimeManager>(this, true);
        }

        private void InitializeComponent()
        {
            this.GetService<IServiceContainer>().AddService<ContainerFilterService>(new InstanceTrackerFilter());
            _Trackers = new TrackerCollection();
            _Policies = new Dictionary<InstanceId, ITrackingPolicy>();

        }

        #region Inner types

        private class InstanceTrackerFilter : ContainerFilterService
        {
            private static readonly ComponentCollection EmptyComponentCollection = new ComponentCollection(new IComponent[] { });
            public override ComponentCollection FilterComponents(ComponentCollection components)
            {
                return EmptyComponentCollection;
            }
        }

        private class TrackerCollection : KeyedCollection<InstanceId, IInstanceTracker>
        {
            public TrackerCollection()
                : base(x => x.InstanceId)
            { }

            public override string ToString()
            {
                return "Count = " + Count.ToString();
            }
        }

        #endregion

        #region ILifetimeManager Members

        private TrackerCollection _Trackers;
        private Dictionary<InstanceId, ITrackingPolicy> _Policies;

        /// <summary></summary>
        public void Register(InstanceId instanceId, object instance)
        {
            Ensure.NotDisposed(this);
            Ensure.NotNull(instanceId, "instanceId");
            ITrackingPolicy trackingPolicy = null;

            if (!_Policies.TryGetValue(instanceId, out trackingPolicy))
            {
                trackingPolicy = DefaultTrackingPolicies.Permanent;
            }

            Register(instanceId, instance, trackingPolicy);
        }

        /// <summary></summary>
        public void Register(InstanceId instanceId, object instance, ITrackingPolicy trackingPolicy)
        {
            Ensure.NotDisposed(this);
            Ensure.NotNull(instanceId, "instanceId");
            Ensure.NotNull(instance, "instance");
            Ensure.NotNull(trackingPolicy, "trackingPolicy");

            IInstanceTracker tracker = null;
            if (_Trackers.TryGetValue(instanceId, out tracker))
            {
                object target = null;
                if (tracker.TryGetTarget(out target))
                    throw new ArgumentException("instanceId already used");
                else
                    tracker.Dispose();
            }

            IReference reference = trackingPolicy.Apply(instance);
            if (reference.IsValid)
            {
                tracker = new InstanceTracker(instanceId, reference);
                tracker.Disposed += (sender, e) => {   _Trackers.Remove((IInstanceTracker)sender);   };
                _Trackers.Add(tracker);
                base.Add(tracker);
            }

        }

        /// <summary></summary>
        public void Unregister(InstanceId instanceId)
        {
            Ensure.NotDisposed(this);
            Ensure.NotNull(instanceId, "instanceId");
            
                IInstanceTracker tracker = null;
                if (_Trackers.TryGetValue(instanceId, out tracker))
                    tracker.Dispose();
             
        }

        /// <summary></summary>
        public object GetInstance(InstanceId instanceId)
        {
            Ensure.NotDisposed(this);
            Ensure.NotNull(instanceId, "instanceId");
            
                IInstanceTracker tracker = null;
                if (_Trackers.TryGetValue(instanceId, out tracker))
                {
                    object target = null;
                    if (tracker.TryGetTarget(out target))
                        return target;
                    else
                        tracker.Dispose();
                }
                return null;
            
        }

        /// <summary></summary>
        public void SetTrackingPolicy(InstanceId instanceId, ITrackingPolicy trackingPolicy)
        {
            Ensure.NotDisposed(this);
            Ensure.NotNull(instanceId, "instanceId");
            
                if (trackingPolicy == null)
                    _Policies.Remove(instanceId);
                else
                    _Policies[instanceId] = trackingPolicy;
             
        }

        /// <summary></summary>
        public ITrackingPolicy GetTrackingPolicy(InstanceId instanceId)
        {
            Ensure.NotDisposed(this);
            Ensure.NotNull(instanceId, "instanceId");
             
                ITrackingPolicy policy = null;
                _Policies.TryGetValue(instanceId, out policy);
                return policy;
            
        }

        #endregion
    }
}
